package cn.mw.cmdb.entity;

import cn.mw.cmdb.enums.ModelViewTypeEnum;
import cn.mw.cmdb.enums.TableViewTypeEnum;
import cn.mw.cmdb.enums.ViewTypeEnum;
import cn.mw.cmdb.exception.PropertyRepeatException;
import cn.mw.cmdb.param.MwModelLabelValueParam;
import cn.mw.cmdb.service.ModelManage;
import cn.mw.cmdb.service.ServiceChoose;
import cn.mw.cmdb.service.impl.CmdbServiceManage;
import cn.mw.cmdb.service.impl.CustomModelServiceImpl;
import cn.mw.cmdb.service.impl.ModelPropertyServiceImpl;
import cn.mw.cmdb.util.DataCreateInfo;
import cn.mw.components.mongodb.anno.AutoId;
import cn.mw.components.mongodb.anno.CMDBProperty;
import cn.mw.components.mongodb.config.MongoConstant;
import cn.mw.microMonitorCommon.api.cmdb.TreeView;
import cn.mw.microMonitorCommon.api.cmdb.TreeViewType;
import cn.mw.microMonitorCommon.util.SpringUtils;
import com.google.common.base.Strings;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.map.ListOrderedMap;
import org.springframework.data.annotation.Transient;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import static cn.mw.cmdb.param.FunctionModuleEnum.getModuleName;
import static cn.mw.cmdb.service.impl.ModelPropertyServiceImpl.propertyListSort;

@Slf4j
@Data
@Document("mw_cmdbmd_model")
public class ModelInfo extends DataAuth implements ServiceChoose, TreeView, DataCreateInfo {

    public static final String IdPathSep = ",";
    public static final String prefix = "mw_";
    public static final String PropertyMap = "properties";

    @Schema(description = "模型id")
    @CMDBProperty(id = true, searchType = PropertyTypeString.ID, searchPrefix = "_")
    @AutoId
    protected String id;

    @Schema(description = "模型名称")
    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    protected String modelName;

    @Schema(description = "模型编码")
    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    protected String modelCode;

    @Schema(description = "模型分组id")
    @CMDBProperty(searchType = PropertyTypeString.ID)
    protected String groupId;

    @Schema(description = "模型分组id路径")
    @CMDBProperty(searchType = PropertyTypeList.ID)
    protected List<String> groupPath;

    //继承模型id,父模型的父id
    @Schema(description = "继承模型id")
    @CMDBProperty(searchType = PropertyTypeList.ID)
    protected List<String> inheritModelIds;

    @Schema(description = "父模型id列表")
    @CMDBProperty(searchType = PropertyTypeList.ID)
    protected List<String> parentModelIds;

    //模型类型
    @Schema(description = "模型类型,父类型:Parent, 普通类型:Normal")
    @CMDBProperty(searchType = PropertyTypeString.ID)
    protected ModelType modelType;

    //内置:buildin ,自定义:custom
    @Schema(description = "模型级别")
    @CMDBProperty
    protected ModelScope modelScope;

    @Schema(description = "模型说明")
    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    protected String spec;

    @Schema(description = "模型图标")
    @CMDBProperty
    protected String icon;

    @Schema(description = "模型视图")
    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    protected ModelViewTypeEnum modelViewType;

    @Schema(description = "模型功能模块", example = "[\"enableMonitor\" ,\"enableAutomate\" ,\"enableConfigManage\" ,\"enableLogManage\",\"enableFlow\"]}")
    @CMDBProperty(searchType = PropertyTypeList.ID)
    private List<String> functionModule;

    //自定义属性,如果需要获取包含父级的属性,需要通过getPropertyInfo方法
    @Schema(description = "模型属性信息,参照属性添加接口")
    @CMDBProperty(PropertyMap)
    protected Map<String, PropertyInfo> properties = new ListOrderedMap();

    //实例数据处理器
    @CMDBProperty
    protected List<String> insDetailProcessors;

    //模型属性分类排序
    @CMDBProperty
    protected List<String> modelPropertyCatologList;

    //模型存储访问
    @Transient
    protected ModelManage modelManage;

    //通过资产视图查看模型时,控制是否可见
    @CMDBProperty(searchType = PropertyTypeSwitch.ID)
    private Boolean display = true;

    @Schema(description = "流程纳管开关")
    @CMDBProperty(searchType = PropertyTypeSwitch.ID)
    private Boolean enableProManage = false;

    @CMDBProperty(logicDel = true)
    protected Boolean delFlag = false;

    private int count;

    private String itemAssetsStatus;

    private List<String> childStatus;

    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    @Schema(description = "创建人")
    protected Long creater;

    @CMDBProperty(searchType = PropertyTypeTime.ID)
    @Schema(description = "创建时间")
    protected Date createTime;

    @CMDBProperty(searchType = PropertyTypeString.ID, keywordSearch = true)
    @Schema(description = "修改人")
    protected Long modifier;

    @CMDBProperty(searchType = PropertyTypeTime.ID)
    @Schema(description = "修改时间")
    protected Date modificationTime;

    private String selectType;
    @CMDBProperty(searchType = PropertyTypeInt.ID)
    @Schema(description = "模型排序")
    private Integer sortNum;

    //模型验证
    public boolean validateSchema() {
        return true;
    }

    private List<PropertyInfo> doGetParentPropertyInfoList(Consumer<ModelInfoContext> consumer) throws Exception {
        List<String> ids = new ArrayList<>();
        if (null != inheritModelIds) {
            ids.addAll(inheritModelIds);
        }

        if (null != parentModelIds) {
            ids.addAll(parentModelIds);
        }
        //根据属性名称去重,后面的属性名称会覆盖之前的
        List<PropertyInfo> propertyInfoList = new ArrayList<>();
        if (ids.size() > 0) {
            ModelManage modelManageBean = SpringUtils.getBean(ModelManage.class);
            //获取父模型属性信息
            if (modelManageBean != null) {
                List<ModelInfo> parentModelInfos = modelManageBean.findModelInfoByIds(ids);
                if (null != parentModelInfos) {
                    for (ModelInfo parent : parentModelInfos) {
                        for (PropertyInfo propertyInfo : parent.getCurrentModelPropertyInfo()) {
                            if (ModelType.Parent.equals(parent.getModelType())) {
                                //属性来源，父模型时，设置父模型名称
                                propertyInfo.setPropertySource(parent.getModelName());
                                //设置属性模型id
                                propertyInfo.setModelId(parent.getId());
                            }
                            ModelInfoContext modelInfoContext = new ModelInfoContext(propertyInfo, propertyInfoList);
                            consumer.accept(modelInfoContext);
                        }
                    }
                }
            }
        }
        return propertyInfoList;
    }


    private List<PropertyInfo> checkMatch(List<PropertyInfo> list,Consumer<ModelInfoContext> consumer) {
        //根据属性名称去重,后面的属性名称会覆盖之前的
        List<PropertyInfo> propertyInfoList = new ArrayList<>();
        for (PropertyInfo propertyInfo : list) {
            ModelInfoContext modelInfoContext = new ModelInfoContext(propertyInfo, propertyInfoList);
            consumer.accept(modelInfoContext);
        }
        return propertyInfoList;
    }


    //获取本体属性属性
    private List<PropertyInfo> doGetOwnPropertyInfoList(Consumer<ModelInfoContext> consumer) throws Exception {
        List<PropertyInfo> propertyList = new ArrayList<>();
        //获取当前类的模型属性信息
        for (PropertyInfo propertyInfo : properties.values()) {
            propertyInfo.setPropertySource(propertyInfo.isSysInternal() ? ModelScope.buildin.getName() : ModelScope.custom.getName());
            propertyInfo.setModelId(id);
            ModelInfoContext modelInfoContext = new ModelInfoContext(propertyInfo, propertyList);
            consumer.accept(modelInfoContext);
        }
        return propertyList;
    }


    @Transient
    public PropertyInfo getPropertyInfo(String id) throws Exception {
        List<PropertyInfo> collect = getPropertyInfoListById().stream().filter(s -> s.getId().equals(id)).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(collect) && collect.size() == 1) {
            return collect.get(0);
        } else {
            throw new PropertyRepeatException(id);
        }
    }

    //获取模型属性信息
    @Transient
    public List<PropertyInfo> getPropertyInfoList() throws Exception {
        Consumer<ModelInfoContext> consumer = (context) -> {
            PropertyInfo propertyInfo = context.getPropertyInfo();
            context.getPropertyList().add(propertyInfo);
        };
        List<PropertyInfo> parentPropertyInfoMap = doGetParentPropertyInfoList(consumer);
        List<PropertyInfo> ownPropertyInfoMap = doGetOwnPropertyInfoList(consumer);
        parentPropertyInfoMap.addAll(ownPropertyInfoMap);
        CmdbServiceManage cmdbServiceManage = SpringUtils.getBean(CmdbServiceManage.class);
        ModelPropertyServiceImpl propertyService = (ModelPropertyServiceImpl) cmdbServiceManage.getModelService(ModelPropertyServiceImpl.ID);
        propertyService.getPropertyCustomInfo(parentPropertyInfoMap, id);
        return parentPropertyInfoMap;
    }


    //获取模型所有属性信息(本体+父模型的列表)
    @Transient
    public List<PropertyInfo> getAllShowListPropertyInfo(String viewType) throws Exception {
        Consumer<ModelInfoContext> consumer = (context) -> {
            PropertyInfo propertyInfo = context.getPropertyInfo();
            context.getPropertyList().add(propertyInfo);
        };
        List<PropertyInfo> parentPropertyInfoMap = doGetParentPropertyInfoList(consumer);
        List<PropertyInfo> ownPropertyInfoMap = doGetOwnPropertyInfoList(consumer);
        parentPropertyInfoMap.addAll(ownPropertyInfoMap);
        //设置自定义化属性
        CmdbServiceManage cmdbServiceManage = SpringUtils.getBean(CmdbServiceManage.class);
        ModelPropertyServiceImpl propertyService = (ModelPropertyServiceImpl) cmdbServiceManage.getModelService(ModelPropertyServiceImpl.ID);
        propertyService.getPropertyCustomInfo(parentPropertyInfoMap, id);
        final String viewTpeStr = viewType;
        Consumer<ModelInfoContext> consumerDis = (context) -> {
            PropertyInfo propertyInfo = context.getPropertyInfo();
            String viewTypeStr = viewTpeStr != null && viewTpeStr.equals(ViewTypeEnum.financeView.name()) ? ViewTypeEnum.financeView.name() : ViewTypeEnum.instanceView.name();
            if(propertyInfo.getFieldViewAuthList()!=null){
                for (FieldViewAuthVo fieldViewAuthVo : propertyInfo.getFieldViewAuthList()) {
                    if (viewTypeStr.equals(fieldViewAuthVo.getViewType())) {
                        propertyInfo.setFieldViewAuthTypes(fieldViewAuthVo.getFieldViewAuthTypes());
                        break;
                    }
                }
            }
            //获取列表显示的所有属性
            if (propertyInfo.getFieldViewAuthTypes() != null && propertyInfo.getFieldViewAuthTypes().contains(FieldViewAuthType.list)) {
                context.getPropertyList().add(propertyInfo);
            }
        };

        List<PropertyInfo> propertyInfoList = checkMatch(parentPropertyInfoMap, consumerDis);
        return propertyInfoList;
    }

    @Transient
    public List<PropertyInfo> propertyListBySort(List<PropertyInfo> propertyInfos) throws Exception {
        propertyListSort(propertyInfos, PropertyInfo.class);
        return propertyInfos;
    }

    //获取模型属性信息
    @Transient
    public List<PropertyInfo> getPropertyInfoListById() throws Exception {
        Consumer<ModelInfoContext> consumer = (context) -> {
            PropertyInfo propertyInfo = context.getPropertyInfo();
            context.getPropertyList().add(propertyInfo);
        };
        List<PropertyInfo> parentPropertyInfoMap = doGetParentPropertyInfoList(consumer);
        List<PropertyInfo> ownPropertyInfoMap = doGetOwnPropertyInfoList(consumer);
        parentPropertyInfoMap.addAll(ownPropertyInfoMap);
        CmdbServiceManage cmdbServiceManage = SpringUtils.getBean(CmdbServiceManage.class);
        ModelPropertyServiceImpl propertyService = (ModelPropertyServiceImpl) cmdbServiceManage.getModelService(ModelPropertyServiceImpl.ID);
        propertyService.getPropertyCustomInfo(parentPropertyInfoMap, id);
        return parentPropertyInfoMap;
    }

    @Transient
    public Map<String, PropertyInfo> getPropertyInfoById() throws Exception {
        List<PropertyInfo> propertyInfoListById = getPropertyInfoListById();
        Map<String, PropertyInfo> collect = propertyInfoListById.stream().collect(Collectors.toMap(s -> s.getId(), s -> s, (k1, k2) -> k1));
        return collect;
    }

    @Transient
    public List<PropertyInfo> getPropertyInfoListFilterPrefix() throws Exception {
        Consumer<ModelInfoContext> consumer = (context) -> {
            PropertyInfo propertyInfo = context.getPropertyInfo();
            //过滤掉mw_的内置纳管属性
            if (!propertyInfo.getId().startsWith(prefix)) {
                context.getPropertyList().add(propertyInfo);
            }
        };
        List<PropertyInfo> parentPropertyInfoMap = doGetParentPropertyInfoList(consumer);
        List<PropertyInfo> ownPropertyInfoMap = doGetOwnPropertyInfoList(consumer);
        parentPropertyInfoMap.addAll(ownPropertyInfoMap);
        CmdbServiceManage cmdbServiceManage = SpringUtils.getBean(CmdbServiceManage.class);
        ModelPropertyServiceImpl propertyService = (ModelPropertyServiceImpl) cmdbServiceManage.getModelService(ModelPropertyServiceImpl.ID);
        propertyService.getPropertyCustomInfo(parentPropertyInfoMap, id);
        return parentPropertyInfoMap;
    }


    public List<PropertyInfo> getCurrentModelPropertyInfo() {
        List<PropertyInfo> collect = properties.values().stream().collect(Collectors.toList());
        return collect;
    }

    public Map<String, String> getCurrentModelPropertyKeyMapWithInstancePrefix(ModelInfoFilterPropertyId filterPrefix) {
        Map map = null;
        if (null != properties) {
            map = new LinkedHashMap();
            String prefix = null;
            for (PropertyInfo propertyInfo : properties.values()) {
                if (filterPrefix.filterData(propertyInfo.getId())) {
                    continue;
                }
                if (filterPrefix.fiterPrefix(propertyInfo.getId())) {
                    prefix = "";
                } else {
                    prefix = InstanceInfo.SearchPrefix + MongoConstant.FieldSep;
                }
                String value = StringUtils.hasText(propertyInfo.getDesc()) ? propertyInfo.getDesc() : propertyInfo.getName();
                map.put(prefix + propertyInfo.getId(), value);
            }
        }
        return map;
    }

    public String convertModelPropertyKeyMapWithInstancePrefix(String propertyId, ModelInfoFilterPropertyId filterPrefix) {
        String prefix = null;
        if (filterPrefix.fiterPrefix(propertyId)) {
            prefix = "";
        } else {
            prefix = InstanceInfo.SearchPrefix + MongoConstant.FieldSep;
        }

        return prefix + propertyId;
    }

    /**
     * 获取所有列表显示的字段属性
     *
     * @return
     */
    public List<PropertyInfo> getListShowPropertyInfos() {
        return properties.values().stream().filter(s -> s.getFieldViewAuthTypes() != null && s.getFieldViewAuthTypes().contains(FieldViewAuthType.list)).collect(Collectors.toList());
    }

    public void addPropertyInfo(PropertyInfo propertyInfo) {
        properties.put(propertyInfo.getId(), propertyInfo);
    }

    public void addInsDetailProcessorsById(String id) {
        if (null == insDetailProcessors) {
            insDetailProcessors = new ArrayList<>();
        }
        insDetailProcessors.add(id);
    }

    public void initInternalProperties() {
        //服务标识
        PropertyInfo propertyInfo = new PropertyInfo();
        propertyInfo.setId(CmdbServiceManage.serviceId);
        propertyInfo.setName(CmdbServiceManage.serviceId);
        propertyInfo.setPropertyTypeRef(new PropertyTypeRef(PropertyTypeString.ID));
        propertyInfo.setSysInternal(true);
        addPropertyInfo(propertyInfo);
    }

    @Override
    public String getServiceId() {
        return CustomModelServiceImpl.ID;
    }

    @Override
    public Object getParentId() {
        return this.groupId;
    }

    @Override
    public String getItemName() {
        return this.modelName;
    }

    @Override
    public String getType() {
        return TreeViewType.model.name();
    }

    @Override
    public String getViewType() {
        return TableViewTypeEnum.list.getCode();
    }

    public void setGroupPath(List<String> groupPath) {
        this.groupPath = groupPath;
        this.groupId = groupPath.get(groupPath.size() - 1);
    }

    @Transient
    public List<MwModelLabelValueParam> getFunctionModuleList() throws Exception {
        List<MwModelLabelValueParam> list = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(functionModule)) {
            for (String strFunc : functionModule) {
                MwModelLabelValueParam moduleParam = new MwModelLabelValueParam();
                moduleParam.setValue(strFunc);
                moduleParam.setLabel(getModuleName(strFunc));
                list.add(moduleParam);
            }
        }
        return list;
    }

    @Override
    public List<String> getChildStatus() {
        return this.childStatus;
    }

    @Override
    public void setChildStatus(List<String> status) {
        this.childStatus = status;
    }

    private class ModelInfoContext {
        private PropertyInfo propertyInfo;
        private Map<String, PropertyInfo> propertyInfoMap;

        private List<PropertyInfo> propertyList;

        public ModelInfoContext(PropertyInfo propertyInfo, Map<String, PropertyInfo> propertyInfoMap, List<PropertyInfo> propertyList) {
            this.propertyInfo = propertyInfo;
            this.propertyInfoMap = propertyInfoMap;
            this.propertyList = propertyList;
        }

        public ModelInfoContext(PropertyInfo propertyInfo, List<PropertyInfo> propertyList) {
            this.propertyInfo = propertyInfo;
            this.propertyList = propertyList;
        }

        public PropertyInfo getPropertyInfo() {
            return propertyInfo;
        }

        public List<PropertyInfo> getPropertyList() {
            return propertyList;
        }

        public Map<String, PropertyInfo> getPropertyInfoMap() {
            return propertyInfoMap;
        }
    }
}
